/*
 * CPLEXProblemGenerator.c
 *
 *  Created on: 04/giu/2013
 *      Author: Ialenti Andrea, Marangon Fabio
 */

#include "../headers/CPLEXProblemGenerator.h"
#include "../headers/Graph.h"
#include "../headers/Heuristic.h"
#include "../headers/Lagrangian.h"
#include "../headers/OneTree.h"
#include "../headers/StatusCodes.h"
#include "../headers/Utility.h"

#include <math.h>
#include <string.h>

Status createCorrespondences(const Config *cfg, Problem *p, const Graph *g, bool verbose) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "createCorrespondences");

	int i, j, idx;
	for (i = 0; i < g->size; i++)
		p->pos[i][0] = p->pos[i][1] = -1;

	//	Populate the position matrix.
	for (i = 0, idx = 0; i < g->size - 1; i++)
		for (j = i + 1; j < g->size; j++, idx++)

			//	Consider only the edges with cost greater than zero and less than infinity.
			if (!(g->edges[i][j].cost > -cfg->EPS && g->edges[i][j].cost < cfg->EPS) && g->edges[i][j].cost < cfg->INF) {

				//	Store the edges and the corresponding costs.
				p->pos[idx][0] = i;
				p->pos[idx][1] = j;
				p->Xe[idx] = idx;
				p->XeCost[idx] = g->edges[i][j].cost;
			} else
				idx--;

	if (verbose)
		fprintf(stdout,	"\t'%s': correspondences created between 'Xe' variables and graph edges. . .\n", funcName);

	freeCharArray(funcName);
	return SUCCESS;
}

void getEdgeNodes(const Problem *p, int XeIdx, int *i, int *j) {

	//	Retrieve the edge indices (i, j).
	*i = p->pos[XeIdx][0];
	*j = p->pos[XeIdx][1];
	return;
}

int getEdgeIndex(const Problem *p, int i, int j) {
	int startIdx, endIdx, mid, oldMid;

	//	It is necessary that the node 'i' is the one with lesser index.
	if (i > j)
		swapInt(&i, &j);

	//	Reset indices.
	startIdx = 0;
	endIdx = p->numcols - 1;
	if (endIdx < 0)
		return -1;

	//	Search the index of a 'Xe' that is connected to the node 'i' (all the 'Xe' connected to the same node 'i' are sequential).
	while (startIdx <= endIdx) {
		mid = floor((startIdx + endIdx) / 2);

		if (p->pos[mid][0] == i)
			break;
		else if (p->pos[mid][0] < i)
			startIdx = mid + 1;
		else
			endIdx = mid - 1;
	}
	oldMid = mid + 1;

	//	Search 'j' going backward.
	while (mid > -1)
		if (p->pos[mid][0] == i) {
			if (p->pos[mid--][1] == j)
				return mid + 1;
		} else
			break;

	//	Search 'j' going forward.
	while (oldMid < p->numcols)
		if (p->pos[oldMid][0] == i) {
			if (p->pos[oldMid++][1] == j)
				return oldMid - 1;
		} else
			break;

	return -1;
}

Status setProbData(const Config *cfg, Problem *p) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "setProbData");
	int i, j, k, onesCnt;

	p->nonZero = 0;

	//	Set number of rows, columns and problem type.
	p->numcols = p->edges;
	p->numrows = p->nodes;
	p->objsen = CPX_MIN;

	//	Set the object function coefficients, and the variables type (with lower and upper bounds).
	for (i = 0; i < p->numcols; i++) {

		p->objCoeff[i] = p->XeCost[i];
		p->ctype[i] = 'B';
		p->lb[i] = 0.0;
		p->ub[i] = 1.0;
	}

	//	Set constraints right hand side values (that is '= 2') and constraint names.
	for (i = 0; i < p->numrows; i++) {
		p->rhs[i] = 2.0;
		p->sense[i] = 'E';

		//	Set the constraint names.
		sprintf(p->constrNames[i], "c_node_%d", i);
		p->constrIdx[i] = i;
	}

	//	Create and initialize to zero a temporary matrix used to build the CPLEX constraint matrix.
	int **tmpMat = initMat(p->numrows, p->numcols);
	if (tmpMat == NULL) {
		fprintf(stdout, "***'%s': error allocating the temporary matrix to create the initial problem!\n", funcName);
		freeCharArray(funcName);
		return ERR_MEM_ALLOC;
	}

	//	Count the total number of non-zero coefficients in the problem constraints and update the temporary matrix (in column-major).
	for (i = 0; i < p->numcols; i++)
		if (p->pos[i][0] != -1 && p->pos[i][1] != -1) {
			tmpMat[p->pos[i][0]][i] = tmpMat[p->pos[i][1]][i] = 1;
			p->nonZero += 2;

			//	Set the variables names.
			sprintf(p->varNames[i], "x(n%d,n%d)", p->pos[i][0], p->pos[i][1]);
		}

	//	Initialize the last 2 arrays of the 'Problem' data structure.
	p->matind = (int*) calloc(p->nonZero, sizeof(int));
	p->matval = (double*) calloc(p->nonZero, sizeof(double));
	if (p->matind == NULL || p->matval == NULL) {
		fprintf(stdout, "***'%s': memory error trying to create the initial problem!\n", funcName);
		freeCharArray(funcName);
		return ERR_MEM_ALLOC;
	}

	//	Create the CPLEX constraint matrix setting the arrays 'matbeg', 'matval', 'matind' and 'matcnt'.
	p->matbeg[0] = 0;
	for (j = 0, k = 0; j < p->numcols; j++) {
		for (i = 0, onesCnt = 0; i < p->numrows; i++)

			//	Count the non-zero coefficients from the temporary matrix in colum-major.
			if (tmpMat[i][j] == 1) {
				onesCnt++;

				p->matval[k] = 1.0;
				p->matind[k++] = i;
			}

		p->matcnt[j] = onesCnt;
		if (j + 1 < p->numcols)
			p->matbeg[j + 1] = p->matbeg[j] + onesCnt;
	}
	freeCharArray(funcName);
	return SUCCESS;
}

Status setProxyProbData(const Config *cfg, Problem *psP, const double *x) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "setProxyProbData");
	int i, j, k, onesCnt;

	psP->nonZero = 0;

	//	Set number of rows, columns and problem type.
	psP->numcols = psP->edges;
	psP->numrows = psP->nodes;
	psP->objsen = CPX_MIN;

	//	Set the object function coefficients, and the variables type (with lower and upper bounds).
	for (i = 0; i < psP->numcols; i++) {
		if (x[i] == 1)
			psP->objCoeff[i] = -1;
		if (x[i] == 0)
			psP->objCoeff[i] = 1;

		psP->ctype[i] = 'B';
		psP->lb[i] = 0.0;
		psP->ub[i] = 1.0;
	}

	//	Set constraints right hand side values (that is '= 2') and constraint names.
	for (i = 0; i < psP->numrows; i++) {
		psP->rhs[i] = 2.0;
		psP->sense[i] = 'E';

		//	Set the constraint names.
		sprintf(psP->constrNames[i], "c_node_%d", i);
		psP->constrIdx[i] = i;
	}

	//	Create and initialize to zero a temporary matrix used to build the CPLEX constraint matrix.
	int **tmpMat = initMat(psP->numrows, psP->numcols);
	if (tmpMat == NULL) {
		fprintf(stdout, "***'%s': error allocating the temporary matrix to create the initial problem!\n", funcName);
		freeCharArray(funcName);
		return ERR_MEM_ALLOC;
	}

	//	Count the total number of non-zero coefficients in the problem constraints and update the temporary matrix (in column-major).
	for (i = 0; i < psP->numcols; i++)
		if (psP->pos[i][0] != -1 && psP->pos[i][1] != -1) {
			tmpMat[psP->pos[i][0]][i] = tmpMat[psP->pos[i][1]][i] = 1;
			psP->nonZero += 2;

			//	Set the variables names.
			sprintf(psP->varNames[i], "x(n%d,n%d)", psP->pos[i][0], psP->pos[i][1]);
		}

	//	Initialize the last 2 arrays of the 'Problem' data structure.
	psP->matind = (int*) calloc(psP->nonZero, sizeof(int));
	psP->matval = (double*) calloc(psP->nonZero, sizeof(double));
	if (psP->matind == NULL || psP->matval == NULL) {
		fprintf(stdout, "***'%s': memory error trying to create the initial problem!\n", funcName);
		freeCharArray(funcName);
		return ERR_MEM_ALLOC;
	}

	//	Create the CPLEX constraint matrix setting the arrays 'matbeg', 'matval', 'matind' and 'matcnt'.
	psP->matbeg[0] = 0;
	for (j = 0, k = 0; j < psP->numcols; j++) {
		for (i = 0, onesCnt = 0; i < psP->numrows; i++)

			//	Count the non-zero coefficients from the temporary matrix in colum-major.
			if (tmpMat[i][j] == 1) {
				onesCnt++;

				psP->matval[k] = 1.0;
				psP->matind[k++] = i;
			}

		psP->matcnt[j] = onesCnt;
		if (j + 1 < psP->numcols)
			psP->matbeg[j + 1] = psP->matbeg[j] + onesCnt;
	}

	//	Add the proximity search constraint.
	psP->heuSol = psP->probUB;
	psP->beg[1] = 0;

	for (i = 0; i < psP->numcols; i++) {
		psP->vals[i] = psP->XeCost[i];
		psP->idx[i] = i;
	}

	sprintf(psP->psName[0], cfg->CPX_PS_C_NAME);

	freeCharArray(funcName);
	return SUCCESS;
}

Status generateProb(const Config *cfg, CPXENVptr *env, CPXLPptr *lp, Problem *p, const Graph *g, double LB, double UB, bool useCB, bool verbose) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "generateProb");
	time_t t1, t2;
	double execTime, normTime;
	int cpxStat;
	Status status;

	fprintf(stdout, "\n\t'%s': creating the CPLEX problem, without the SECs. . .\n", funcName);
	time(&t1);

	//	Set some problem data.
	p->probLB = LB;
	p->probUB = UB;
	strcpy(p->name, g->name);
	char *probName = initCharArray(cfg->NAME_LEN);
	sprintf(probName, "TSP problem '%s'.", g->name);

	//	Create the CPLEX *environment and check if errors occurred.
	if (*env == NULL) {
		*env = CPXopenCPLEX(&cpxStat);
		if (*env == NULL) {
			char errMsg[CPXMESSAGEBUFSIZE];
			CPXgeterrorstring(*env, cpxStat, errMsg);
			fprintf(stdout, "***'%s': could not open CPLEX *environment, error: %s.\n", funcName, errMsg);
			freeCharArray(funcName);
			freeCharArray(probName);
			return ERR_CPX;
		}
	}

	//	Set the node-files to be compressed and written on disk.
	cpxStat = CPXsetintparam(*env, CPX_PARAM_NODEFILEIND, 3);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set node-files indicator, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_TRELIM, cfg->CPX_MEM_LIM);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set memory limit, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	If callbacks are active, force CPLEX to use the original model, not the reduced one and turn off CPLEX pre-solver.
	if (useCB) {
		cpxStat = CPXsetintparam(*env, CPX_PARAM_MIPCBREDLP, CPX_OFF);
		if (cpxStat != CPX_SUCCESS) {
			fprintf(stdout, "***'%s': failed to disable 'MIPCBREDLP', error #: %d.\n", funcName, cpxStat);
			freeCharArray(funcName);
			freeCharArray(probName);
			return ERR_CPX;
		}
		cpxStat = CPXsetintparam(*env, CPX_PARAM_PRELINEAR, CPX_OFF);
		if (cpxStat != CPX_SUCCESS) {
			fprintf(stdout, "***'%s': failed to disable 'PRELINEAR', error #: %d.\n", funcName,	cpxStat);
			freeCharArray(funcName);
			freeCharArray(probName);
			return ERR_CPX;
		}
	}

	//	Turn on/off CPLEX output to the standard output (console) based on the desired verbosity.
	CPXINT On_Off = verbose ? CPX_ON : CPX_OFF;
	cpxStat = CPXsetintparam(*env, CPX_PARAM_SCRIND, On_Off);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to turn on/off screen indicator, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Set the number of threads to be used.
	cpxStat = CPXsetintparam(*env, CPX_PARAM_THREADS, cfg->CPX_TH_CNT);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set thread number, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Create the CPLEX problem 'lp'.
	*lp = CPXcreateprob(*env, &cpxStat, probName);
	if (*lp == NULL) {
		fprintf(stdout, "***'%s': failed to create problem lp, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Set the CPLEX time limit.
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_TILIM, cfg->CPX_TIME_LIM / cfg->NORM_TIME);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set the time limit, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Create the problem (populate the 'Problem' data structure).
	status = setProbData(cfg, p);
	if (status != SUCCESS) {
		fprintf(stdout, "***'%s': error (%d) creating the initial problem.\n", funcName, status);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CREATE_PROB;
	}

	//	Copy the problem data into the 'lp'.
	cpxStat = CPXcopylp(*env, *lp, p->numcols, p->numrows, p->objsen, p->objCoeff, p->rhs, p->sense, p->matbeg, p->matcnt, p->matind, p->matval, p->lb, p->ub, NULL);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy problem data. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Copy the 'ctype' array (that is the types of the variables).
	cpxStat = CPXcopyctype(*env, *lp, p->ctype);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy 'ctype'. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Set problem upper and lower bounds.
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_CUTUP, p->probUB);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set upper bound. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_CUTLO, p->probLB);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set upper bound. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Set the variables names.
	cpxStat = CPXchgcolname(*env, *lp, p->numcols, p->Xe, p->varNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the variables names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Set the constraint names.
	cpxStat = CPXchgrowname(*env, *lp, p->numrows, p->constrIdx, p->constrNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the constraint names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Write problem to file.
	sprintf(probName, "files/CPLEX/%s.lp", p->name);
	cpxStat = CPXwriteprob(*env, *lp, probName, "lp");
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error writing problem to file. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		return ERR_CPX;
	}

	//	Print execution time.
	time(&t2);
	if (verbose) {
		execTime = difftime(t2, t1);
		normTime = execTime * cfg->NORM_TIME;
		fprintf(stdout, "\t'%s': problem created in %0.2f second(s) (%0.2f second(s) normalized time)!\n", funcName, execTime, normTime);
	}

	//	Free the memory.
	freeCharArray(funcName);
	freeCharArray(probName);
	return SUCCESS;
}

Status generateProxyProb(const Config *cfg, CPXENVptr *env, CPXLPptr *lp, CPXLPptr *psLP, Problem *p, Problem *psP, const Graph *g, double LB, double UB, const OneTree *tour, bool verbose) {
	char *funcName = initCharArray(cfg->NAME_LEN);
	sprintf(funcName, "generateProxyProb");

	time_t t1, t2;
	double execTime, normTime;
	int cpxStat, i, i1, i2, idx;
	double *x = NULL;
	Status status;

	if (verbose)
		fprintf(stdout, "\n\t'%s': creating the CPLEX proximity search problem. . .\n", funcName);
	time(&t1);

	//	Set some problem data.
	p->probLB = psP->probLB = LB;
	p->probUB = psP->probUB = UB;
	strcpy(p->name, g->name);
	strcpy(psP->name, g->name);

	char *probName = initCharArray(cfg->NAME_LEN);
	char *probNamePS = initCharArray(cfg->NAME_LEN);
	sprintf(probName, "TSP problem '%s'.", g->name);
	sprintf(probNamePS, "TSP problem '%s' (PS).", g->name);

	//	Create the CPLEX *environment and check if errors occurred.
	if (*env == NULL) {
		*env = CPXopenCPLEX(&cpxStat);
		if (*env == NULL) {
			char errMsg[CPXMESSAGEBUFSIZE];
			CPXgeterrorstring(*env, cpxStat, errMsg);
			fprintf(stdout, "***'%s': could not open CPLEX environment. Error: %s.\n", funcName, errMsg);
			freeCharArray(funcName);
			freeCharArray(probName);
			freeCharArray(probNamePS);
			freeDblArray(x);
			return ERR_CPX;
		}
	}

	//	Force CPLEX to use the original model, not the reduced one and turn off CPLEX pre-solver.
	cpxStat = CPXsetintparam(*env, CPX_PARAM_MIPCBREDLP, CPX_OFF);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to disable 'MIPCBREDLP', error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}
	cpxStat = CPXsetintparam(*env, CPX_PARAM_PRELINEAR, CPX_OFF);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to disable 'PRELINEAR', error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Turn off CPLEX output to the standard output (console).
	cpxStat = CPXsetintparam(*env, CPX_PARAM_SCRIND, CPX_OFF);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to turn off screen indicator, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the number of threads to be used.
	cpxStat = CPXsetintparam(*env, CPX_PARAM_THREADS, cfg->CPX_TH_CNT);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set thread number, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the node-files to be compressed and written on disk.
	cpxStat = CPXsetintparam(*env, CPX_PARAM_NODEFILEIND, 3);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set node-files indicator, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Create the CPLEX problem 'lp' and the proximity search problem 'psLP'.
	*lp = CPXcreateprob(*env, &cpxStat, probName);
	if (*lp == NULL) {
		fprintf(stdout, "***'%s': failed to create problem lp, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}
	*psLP = CPXcreateprob(*env, &cpxStat, probNamePS);
	if (*psLP == NULL) {
		fprintf(stdout, "***'%s': failed to create problem psLP, error #: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set problem upper and lower bounds.
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_CUTUP, p->probUB);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set upper bound. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}
	cpxStat = CPXsetdblparam(*env, CPX_PARAM_CUTLO, p->probLB);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to set upper bound. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Retrieve the heuristic solution edges.
	x = initDblArray(p->numcols);
	if (x == NULL) {
		fprintf(stdout, "***'%s': memory error during array initialization.\n", funcName);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_MEM_ALLOC;
	}

	for (i = 0; i < tour->size; i++) {
		i1 = tour->edges[i].start->id;
		i2 = tour->edges[i].end->id;

		idx = getEdgeIndex(p, i1, i2);
		x[idx] = 1;
	}

	//	Create the problem (populate the 'Problem' data structure).
	status = setProbData(cfg, p);
	if (status != SUCCESS) {
		fprintf(stdout, "***'%s': error (%d) creating the initial problem.\n", funcName, status);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CREATE_PROB;
	}

	//	Copy the problem data into the 'lp'.
	cpxStat = CPXcopylp(*env, *lp, p->numcols, p->numrows, p->objsen, p->objCoeff, p->rhs, p->sense, p->matbeg, p->matcnt, p->matind, p->matval, p->lb, p->ub, NULL);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy problem data. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Copy the 'ctype' array (that is the types of the variables).
	cpxStat = CPXcopyctype(*env, *lp, p->ctype);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy 'ctype'. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the variables names.
	cpxStat = CPXchgcolname(*env, *lp, p->numcols, p->Xe, p->varNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the variables names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the constraint names.
	cpxStat = CPXchgrowname(*env, *lp, p->numrows, p->constrIdx, p->constrNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the constraint names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Create the proximity search problem (populate the 'Problem' data structure).
	status = setProxyProbData(cfg, psP, x);
	if (status != SUCCESS) {
		fprintf(stdout, "***'%s': error (%d) creating the initial proximity search problem.\n", funcName, status);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CREATE_PROB;
	}

	//	Copy the problem data into the 'psLP'.
	cpxStat = CPXcopylp(*env, *psLP, psP->numcols, psP->numrows, psP->objsen, psP->objCoeff, psP->rhs, psP->sense, psP->matbeg, psP->matcnt, psP->matind, psP->matval, psP->lb, psP->ub, NULL);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy problem data. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Copy the 'ctype' array (that is the types of the variables).
	cpxStat = CPXcopyctype(*env, *psLP, psP->ctype);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': failed to copy 'ctype'. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the variables names.
	cpxStat = CPXchgcolname(*env, *psLP, psP->numcols, psP->Xe, psP->varNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the variables names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Set the constraint names.
	cpxStat = CPXchgrowname(*env, *psLP, psP->numrows, psP->constrIdx, psP->constrNames);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error changing the constraint names. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Add the proximity search constraint.
	cpxStat = CPXaddrows(*env, *psLP, psP->numcols, 1, psP->numcols, &psP->heuSol, "L", psP->beg, psP->idx, psP->vals, NULL, psP->psName);
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error adding the proximity search constraint. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Write problems to file.
	sprintf(probNamePS, "files/CPLEX/%s_PS.lp", psP->name);
	cpxStat = CPXwriteprob(*env, *psLP, probNamePS, "lp");
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error writing proximity search problem to file. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}
	sprintf(probName, "files/CPLEX/%s.lp", p->name);
	cpxStat = CPXwriteprob(*env, *lp, probName, "lp");
	if (cpxStat != CPX_SUCCESS) {
		fprintf(stdout, "***'%s': error writing problem to file. Error cpxStat: %d.\n", funcName, cpxStat);
		freeCharArray(funcName);
		freeCharArray(probName);
		freeCharArray(probNamePS);
		freeDblArray(x);
		return ERR_CPX;
	}

	//	Print execution time.
	time(&t2);
	if (verbose) {
		execTime = difftime(t2, t1);
		normTime = execTime * cfg->NORM_TIME;
		fprintf(stdout, "\t'%s': problem created in %0.2f second(s) (%0.2f second(s) normalized time). . .\n", funcName, execTime, normTime);
	}

	//	Free the memory.
	freeCharArray(funcName);
	freeCharArray(probName);
	freeCharArray(probNamePS);
	freeDblArray(x);
	return SUCCESS;
}
